﻿namespace Autodesk.Revit.DB;

/// <summary>
/// Autodesk.Revit.DB.XYZ 的扩展方法
/// </summary>
public static class XYZExtensions
{
    /// <summary>
    /// 根据当前XYZ设置新的X并返回
    /// </summary>
    /// <param name="xyz"></param>
    /// <param name="value"></param>
    /// <returns></returns>
    public static XYZ SetX(this XYZ xyz, double value)
    {
        return new XYZ(value, xyz.Y, xyz.Z);
    }

    /// <summary>
    /// 根据当前XYZ设置新的Y并返回
    /// </summary>
    /// <param name="xyz"></param>
    /// <param name="value"></param>
    /// <returns></returns>
    public static XYZ SetY(this XYZ xyz, double value)
    {
        return new XYZ(xyz.X, value, xyz.Z);
    }

    /// <summary>
    /// 根据当前XYZ设置新的Z并返回
    /// </summary>
    /// <param name="xyz"></param>
    /// <param name="value"></param>
    /// <returns></returns>
    public static XYZ SetZ(this XYZ xyz, double value)
    {
        return new XYZ(xyz.X, xyz.Y, value);
    }

    /// <summary>
    /// 判断点是否在线上
    /// </summary>
    /// <param name="xyz"></param>
    /// <param name="line"></param>
    /// <param name="allowableMaximumDifference">允许最大差值</param>
    /// <returns></returns>
    public static bool IsOnLine(this XYZ xyz, Line line, double allowableMaximumDifference = 0.0000001d)
    {
        return Math.Abs(line.GetEndPoint(0).DistanceTo(xyz) + line.GetEndPoint(1).DistanceTo(xyz) - line.Length) < allowableMaximumDifference ? true : false;
    }

    /// <summary>
    /// 获取向量的垂直向量
    /// </summary>
    /// <param name="dir"></param>
    /// <param name="isTopDownPlane">是否为俯视角平面</param>
    /// <returns></returns>
    public static XYZ GetVerticalDirection(this XYZ dir, bool isTopDownPlane = false)
        => isTopDownPlane ? new XYZ(dir.Y + dir.Z, -dir.X + dir.Z, 0) : new XYZ(dir.Y + dir.Z, -dir.X + dir.Z, -dir.X - dir.Y);

    /// <summary>
    /// 返回当前XYZ投射到无线延长的线上的点
    /// </summary>
    /// <param name="xyz"></param>
    /// <param name="line"></param>
    /// <returns></returns>
    public static XYZ ProjectToCurve(this XYZ xyz, Line line)
    {
        Curve temp = line;

        if (temp.IsBound)
        {
            temp = temp.Clone();
            temp.MakeUnbound();
        }

        var result = temp.Project(xyz).XYZPoint;

        temp.Dispose();

        return result;
    }

    /// <summary>
    /// 判断反向
    /// </summary>
    public static bool IsOppositeDirection(this XYZ dir1, XYZ dir2, double allowableMaximumDifference = 0.000001d)
    {
        if (dir1 is null)
            throw new ArgumentNullException(nameof(dir1));

        if (dir2 is null)
            throw new ArgumentNullException(nameof(dir2));

        bool result = false;

        double dotproduct = dir1.Normalize().DotProduct(dir2.Normalize());

        if (Math.Abs(dotproduct + 1) < allowableMaximumDifference)
            result = true;

        return result;
    }

    /// <summary>
    /// 点旋转
    /// </summary>
    /// <param name="point"></param>
    /// <param name="center"></param>
    /// <param name="angle"></param>
    /// <returns></returns>
    public static XYZ Rotate(this XYZ point, XYZ center, double angle)
    {
        double cos = Math.Cos(angle);
        double sin = Math.Sin(angle);

        double x = point.X - center.X;
        double y = point.Y - center.Y;

        double newX = x * cos - y * sin + center.X;
        double newY = x * sin + y * cos + center.Y;

        return new XYZ(newX, newY, point.Z);
    }
}
